﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Net.Sockets;
using System;
using System.Security.Cryptography;
using System.Linq;
using System.Xml.Schema;
using UnityEngine.UI;

public class BillInfo {
	//并不需要每次交易都变化的值（萌物养成计划）
	public string appid = "wxffdcd4fcc759ef72";
	public string mch_id = "1507497551";
	public string key = "nNFaiPB27Zbd9ajJQ8V29kR7W3v4LT9H";

	//每次交易需要变化的值
	public string nonce_str;
	public string sign;
	public string body;
	public string out_trade_no;
	public int total_fee;
	public string spbill_create_ip;
	public string time_start;
	public string notify_url;
	public string trade_type;
}

//channel: 1: weixin; 2: zhifubao
//orderstatus: init; success; failed;


public class Payment : MonoBehaviour {

	private readonly string weChatPaymentWebUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	private readonly string weChatQueryOrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
	private readonly string serverCreateOrderUrl = "http://games.jcgroup.com.cn/dolltv/createorder.php";
	private readonly string wxNotifyUrl = "http://games.jcgroup.com.cn/dolltv/wxnotify.php";

	private string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	private System.Random random = new System.Random();

	private HttpWebRequest request;

	private RawImage displayImage;

	void Start () {
		//准备
		GameObject.Find("QRCodeEncodeController").GetComponent<QRCodeEncodeController>().onQREncodeFinished += OnQRFinished;
		GameObject.Find ("Canvas/Button").GetComponent<Button> ().onClick.AddListener (QueryOrderStatus);
		displayImage = GameObject.Find ("Canvas/RawImage").GetComponent<RawImage> ();

		SubmitOrder ();

	}
	
	void SubmitOrder () {
		//订单信息
		BillInfo bi = new BillInfo();
		bi.nonce_str = GenerateNonce (32);
		bi.body = GetBody ();
		bi.out_trade_no = GetOutTradeNo ();
		bi.total_fee = GetTotalFee ();
		bi.spbill_create_ip = GetSpbillCreateIP ();
		bi.time_start = GetTimeStart();
		bi.notify_url = GetNotifyUrl ();
		bi.trade_type = GetTradeType ();
		bi.sign = GenerateSign (bi);

		string xmlResult = GenerateXml (bi);

		//给微信发送信息订单信息，用于生成二维码
		PostToUrl(weChatPaymentWebUrl, xmlResult);

		//得到微信的返回
		string remoteResp = GetResponseFromRemote();

		//得到用户生成二维码的链接
		XmlDocument doc = new XmlDocument();
		doc.LoadXml(remoteResp);

		XmlNodeList xnl = doc.GetElementsByTagName("code_url");

		if (xnl != null) {
			//显示二维码
			XmlNode xn = xnl.Item(0);
			Debug.Log(xn.InnerText);

			string wxurl = xn.InnerText;
			GameObject.Find("QRCodeEncodeController").GetComponent<QRCodeEncodeController>().Encode(wxurl);

			//保存订单到服务器上
			string orderConent = string.Empty;
			orderConent += "out_trade_no=";
			orderConent += bi.out_trade_no;
			orderConent += "&";
			orderConent += "total_fee=";
			orderConent += bi.total_fee;
			orderConent += "&";
			orderConent += "spbill_create_ip=";
			orderConent += bi.spbill_create_ip;
			orderConent += "&";
			orderConent += "channel=";
			orderConent += "1";
			//TODO: add user name

			PostToUrl(serverCreateOrderUrl, orderConent);
		}
		else {
			Debug.LogWarning("No weixin:// found!");
		}
	}

	private string GenerateXml(BillInfo bi) {

		string result = string.Empty;

		result += "<xml>\n";

		//appid
		result += string.Format ("<appid>{0}</appid>\n", bi.appid);
		result += string.Format ("<mch_id>{0}</mch_id>\n",bi.mch_id);
		result += string.Format ("<nonce_str>{0}</nonce_str>\n", bi.nonce_str);
		result += string.Format ("<body>{0}</body>\n", bi.body);
		result += string.Format ("<out_trade_no>{0}</out_trade_no>\n", bi.out_trade_no);
		result += string.Format ("<total_fee>{0}</total_fee>\n", bi.total_fee);
		result += string.Format ("<spbill_create_ip>{0}</spbill_create_ip>\n", bi.spbill_create_ip);
		result += string.Format ("<time_start>{0}</time_start>\n", bi.time_start);
		result += string.Format ("<notify_url>{0}</notify_url>\n", bi.notify_url);
		result += string.Format ("<trade_type>{0}</trade_type>\n", bi.trade_type);
		result += string.Format ("<sign>{0}</sign>\n", bi.sign);

		result += "</xml>";

		return result;
	}

	private string GetNotifyUrl() {
		return wxNotifyUrl;
	}

	private string GenerateNonce(int length) {
		var nonceString = new StringBuilder ();
		for (int i = 0; i < length; ++i) {
			int index = random.Next (0, validChars.Length - 1);
			nonceString.Append (validChars [index]);
		}
		return nonceString.ToString ();
	}

	private string GenerateSign(BillInfo bi) {

		//step 1:
		Dictionary<string, string> dict = new Dictionary<string, string>();

		dict.Add ("appid", bi.appid);
		dict.Add ("mch_id", bi.mch_id);
		dict.Add ("nonce_str", bi.nonce_str);
		dict.Add ("body", bi.body);
		dict.Add ("out_trade_no", bi.out_trade_no);
		dict.Add ("total_fee", bi.total_fee.ToString());
		dict.Add ("spbill_create_ip", bi.spbill_create_ip);
		dict.Add ("time_start", bi.time_start);
		dict.Add ("notify_url", bi.notify_url);
		dict.Add ("trade_type", bi.trade_type);

		List<KeyValuePair<string, string>> tempList = dict.ToList ();
		tempList.Sort (
			delegate(KeyValuePair<string, string> pair1, KeyValuePair<string, string> pair2) {
				return pair1.Key.CompareTo (pair2.Key);
			}
		);

		string result = string.Empty;
		for (int i = 0; i < tempList.Count; ++i) {
			result += tempList [i].Key;
			result += "=";
			result += tempList [i].Value;
			result += "&";
		}

		result += "key";
		result += "=";
		result += bi.key;

		//step 2:
		byte[] array = System.Text.Encoding.UTF8.GetBytes (result);
		MD5 md5 = MD5.Create ();
		byte[] hash = md5.ComputeHash (array);

		StringBuilder sb = new StringBuilder ();
		for (int i = 0; i < hash.Length; ++i) {
			sb.Append (hash [i].ToString ("X2"));
		}

		return sb.ToString ();
	}

	private string GetBody() {
		return "萌物养成计划TV版-萌宠支付";
	}

	private string GetOutTradeNo() {
		var orderId = DateTime.Now.ToString ("yyyyMMddHHmmssffff") + random.Next (100000, 999999) + GenerateNonce (6);
		return orderId;
	}

	private int GetTotalFee() {
		return 1;
	}

	private string GetSpbillCreateIP() {
		IPHostEntry host = Dns.GetHostEntry (Dns.GetHostName ());
		foreach (var ip in host.AddressList) {
			if (ip.AddressFamily == AddressFamily.InterNetwork) {
				return ip.ToString ();
			}
		}

		return "127.0.0.1";
	}

	private string GetTimeStart() {
		return DateTime.Now.ToString ("yyyyMMddHHmmss");
	}

	private string GetTradeType() {
		return "NATIVE";
	}  

	//event from qr controller
	private void OnQRFinished(Texture2D image) {
		displayImage.texture = image;
	}

	private void QueryOrderStatus() {
		SubmitOrder ();
	}

	private void PostToUrl(string url, string content) {
		byte[] data = Encoding.UTF8.GetBytes(content);
		request = WebRequest.Create (url) as HttpWebRequest;
		request.ServicePoint.ConnectionLimit = 15;
		request.Method = "POST";
		request.Timeout = 10000;
		request.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
		request.ContentLength = data.Length;

		try {
			using (Stream stream = request.GetRequestStream())
			{
				stream.Write(data, 0, data.Length);
			}
		}
		catch (Exception e) {
			Debug.LogError (e);
		}
	}

	private string GetResponseFromRemote() {
		if (request == null)
			return null;

		string remoteResp = string.Empty;
		try {
			WebResponse response;
			Stream respStream;
			byte[] buffer;

			response = request.GetResponse();
			int bufferLength = (int) response.ContentLength;

			if (bufferLength <= 0) {
				Debug.LogError("");
				return null;
			}

			long totalSize = bufferLength;
			buffer = new byte[totalSize];
			respStream = null;
			long readPointer = 0;
			int offset = 0;
			int receivedBytesCount = 0;
			respStream = response.GetResponseStream();
			do
			{
				receivedBytesCount = respStream.Read(buffer, offset, bufferLength - offset);
				offset += receivedBytesCount;
				if (receivedBytesCount > 0)
				{
					readPointer += receivedBytesCount;
				}
			}
			while (receivedBytesCount != 0);

			remoteResp = Encoding.UTF8.GetString(buffer);
		}
		catch (Exception e) {
			Debug.LogError (e.ToString());
		}

		return remoteResp;
	}
}
